/**
**************************************************************************************
*Founder Effect                                                                      *
*Copyright (C) 2006-2007, 5d Studios (www.5d-Studios.com)                            *
*                                                                                    *
*This library is free software; you can redistribute it and/or                       *
*modify it under the terms of the GNU Lesser General Public                          *
*License as published by the Free Software Foundation; either                        *
*version 2.1 of the License, or (at your option) any later version.                  *
*                                                                                    *
*This library is distributed in the hope that it will be useful,                     *
*but WITHOUT ANY WARRANTY; without even the implied warranty of                      *
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                   *
*Lesser General Public License for more details.                                     *
*                                                                                    *
*You should have received a copy of the GNU Lesser General Public                    *
*License along with this library; if not, write to the Free Software                 *
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA      *
**************************************************************************************
*/
#include "StdAfx.h"
#include <istream>
#include <stdio.h>

#include "Scene.h"
#include "IEntity.h"
#include "Tile.h"

#include "EntityManager.h"
#include "../shared/QuadTree.h"
#include "../render/FadeEffect.h"
#include "../Kernel.h"

extern Kernel* g_kernel;

#define random()	((rand () & 0x7fff) / ((float)0x7fff))
#define crandom()	(2.0f * (random() - 0.5f))

bool Scene::m_loading = false;

Scene::Scene(void)
{
    m_init = false;
    m_maxX = 0;
    m_maxY = 0;

    m_tileSize = 64;

    m_secondLayerQuadTree = NULL;
}

/** Init */
void Scene::Init(type_Layer layerOne, type_Layer layerTwo)
{
    FreeScene();
    m_layer1 = layerOne;
    m_layer2 = layerTwo;

    int mapWidth = 0, mapHeight = 0;
    TileToWorld( GetMaxX(), GetMaxY(), mapWidth, mapHeight );

    m_secondLayerQuadTree = new QuadTree<tile_t*>( 12,12, mapWidth, mapHeight );

    // populate based off the size of layer one
    size_t size1 = m_layer1.size();
    size_t size2 = 0;
    if ( !m_layer1.empty() )
        size2 = m_layer1[0].size();

    for( int i = 0; i < size1; ++i )
    {
        for( int j = 0; j < size2; ++j )
        {
            Tile* t = m_layer2[j][i]; // memory thrash
            if ( !t ) continue;

            tile_t* tile = new tile_t;
            tile->image = t->m_image;
            tile->m_id = m_tileReferences.size(); // TODO - Make this a proper id set
            tile->m_vPosition.Set( i*m_tileSize, j*m_tileSize );
            tile->m_bounds = Rect(tile->m_vPosition.x, tile->m_vPosition.y , m_tileSize, m_tileSize );
            m_tileReferences.push_back( tile ); // place in the reference bin for deleting

            m_secondLayerQuadTree->Put( tile );
        }
    }
    m_init = true;
    //FadeEffect g_effect;
    //g_effect.SetEffectSize( Rect(0,0,800,600) );
    //g_effect.SetEffectDuration( g_kernel->GetTime() + 13000 );
    //g_effect.SetEffectTime( 0.004f );
    //g_effect.SetStartColor( Vector4f( 0,0,0,0 ) );
    //g_effect.SetEndColor( Vector4f( 0,0,0,1 ) );

}

/**
===============================================================
Free the current scenes resources
===============================================================
*/
void Scene::FreeScene()
{
    type_Tiles::iterator it = m_tileReferences.begin();
    for(; it != m_tileReferences.end(); ++it )
        delete (*it);
    
    m_tileReferences.clear();
    m_layer1.clear();
    m_layer2.clear();
    m_init = false;

    if ( m_secondLayerQuadTree )
        delete m_secondLayerQuadTree;
    
    EntityManager::GetInstance()->DeleteAllEntities();
}

/** Get the tile at this location in array*/
Tile* Scene::GetTile(int layer, int x, int y)
{
    return (layer > 1) ? m_layer2[y][x] : m_layer1[y][x];
}
/** Get the tile from screen coords */
Tile* Scene::GetScreenTile(int layer, int x, int y)
{
    int wx, wy;
    WorldToTile ( x, y, wx, wy );

    return GetTile( layer, wx, wy );
}
  
/** Test wether entities are allowed on the tile */
bool  Scene::Collide(int x, int y, Rect &r) const
{
    // TODO - check bounds of character
    if ( CheckBounds(x,y) )
        return true;

    int index_x = 0, index_y = 0;
    WorldToTile( x, y , index_x, index_y );

    return  (m_layer2[index_y][index_x] != NULL);

}


/** Check the map boundries */
bool  Scene::CheckBounds(int x, int y) const
{
    // check the map bounds
    return ( x <= 0 || y <= 0  || x >= m_maxX*m_tileSize-m_tileSize || y >= m_maxY*m_tileSize-m_tileSize   );
}


/** Check for a Trigger event */
void  Scene::InvokeTrigger( IEntity* triggerer, int x, int y ) const
{
    int index_x = 0, index_y = 0;
    WorldToTile( x, y , index_x, index_y );

    Tile* tile = m_layer1[index_y][index_x];
    if ( tile )
        tile->Trigger(triggerer, Vector2f(index_x,index_y) );
}

/** convert from world coordinates to tile locations */
void Scene::WorldToTile( int x, int y, int &toIndex_x, int &toIndex_y ) const
{
    // Current Tile offset (to pixels)
    int tileOffset_x =  ( x % m_tileSize );
    // to next index
    toIndex_x    = ( tileOffset_x + x) / m_tileSize;

    // current tile y offset (to pixels)
    int tileOffset_y = ( y % m_tileSize);
    toIndex_y    = (tileOffset_y + y) / m_tileSize;
}

/** Convert Tile coordinates to world coordinates */
void Scene::TileToWorld( int tx, int ty, int &wx, int &wy ) const
{
    wx = tx * m_tileSize;
    wy = ty * m_tileSize;
}

/** Center the camera around our player */
void Scene::CenterCamera( ICamera* cam, Vector2f &pos ) const
{
    // screen dimensions
    float h_height = (cam->GetViewPort().height /2);
    float h_width  = (cam->GetViewPort().width  /2);

    // handle being on top edge of map
    if ( pos.y + h_height > m_maxY*m_tileSize )
        h_height = (pos.y-m_maxY*m_tileSize + h_height*2);
    
    // handle bottom edge
    if ( pos.y - h_height < 0 ) 
        h_height = pos.y;


    // handle left edge
    if ( pos.x - h_width  < 0 )
        h_width = pos.x;


    // handle right edge
    if ( pos.x + h_width  > m_maxX*m_tileSize )
        h_width = pos.x+m_maxX*m_tileSize - h_width*2;

    // adjust the camera
    cam->SetScreenCoord( Vector2f(h_width, h_height) );
    
    // move the camera
    cam->MoveTo( pos );
}



/**
=====================
Draw the current scene given location of the camera
=====================
*/
void Scene::Draw( IRender *r, ICamera* cam ) 
{
    Vector2f camPos = cam->GetPosition();
    Rect viewPort   = cam->GetViewPort();
    // the viewport x, and y location
    int vx = cam->GetViewPort().x;
    int vy = cam->GetViewPort().y;

    // Get the current map offsets
    m_offset.Set( camPos );

    //screen pixel x,y coordinate to draw the current tile to
    int pixel_x = 0;
    int pixel_y = 0;

	int index_x = 0;
    int index_y = 0;

    // Current Tile offset (to pixels)
	int tileOffset_x =  -( (int)camPos.x % m_tileSize );
    // to next index
    int toIndex_x    = ( tileOffset_x + (int)camPos.x) / m_tileSize;

    // current tile y offset (to pixels)
    int tileOffset_y = -( (int)camPos.y % m_tileSize);
    int toIndex_y    = (tileOffset_y + (int)camPos.y) / m_tileSize;
    
    if ( cam->GetID() == 1 )
    {
        r->SetColorEffect( 0.7f, 0.6f, 1 );
    }

    index_y = toIndex_y;
    double dt = g_kernel->GetFrameTime();
    for( pixel_y = tileOffset_y; pixel_y < viewPort.height && index_y < m_maxY;  pixel_y += m_tileSize )
    {
        for( pixel_x = tileOffset_x, index_x = toIndex_x; pixel_x < viewPort.width && index_x < m_maxX;  pixel_x += m_tileSize)
        {
         
            if ( index_y >= 0 && index_x >= 0 )
            {
                // draw the first layer
                if ( m_layer1[index_y][index_x] != NULL )
                {
                    // set the tile to the correct location
                    m_layer1[index_y][index_x]->Set( pixel_x+vx, pixel_y+vy );
                    m_layer1[index_y][index_x]->Update( dt );      // animate the tile         
                    // Render the tile
                    m_layer1[index_y][index_x]->Render( r, cam );
                }
                // draw second layer
                if ( m_layer2[index_y][index_x] != NULL )
                {
                    m_layer2[index_y][index_x]->Set(  pixel_x+vx, pixel_y+vy  );
                    m_layer1[index_y][index_x]->Update( dt ); 
                    m_layer2[index_y][index_x]->Render( r, cam );
                }
            }
            // update the index
            index_x++;

        }
        index_y++;
    }

    if ( g_kernel->GetCvars()->GetFloatValue( "v_debug" ) )
        EntityManager::GetInstance()->GetEntityQuadTree()->Draw( r, cam );
    //int amount = 30;
    //float spread = 60.4f;
    //Vector2f start( 32, 32 );
    //start = start + (EntityManager::GetInstance()->GetPlayer()->m_world );
    //Vector2f forward ( EntityManager::GetInstance()->GetPlayer()->m_forward );
    //for ( int i = 0; i < amount; ++i )
    //{
    //    //float r1 = random() * PI * 2.0f;
    //    //float u = sin(r1); * crandom() * spread * 16.0f;
    //    //r1 = cos(r1) * crandom() * spread * 16.0f;

    //    Vector2f end( sin( /*random() /*/ crandom() )*spread, sin( crandom() )*spread );
    //    end = end + (forward*800);//(forward * 8102.0f * 16.0f) + end;
    //    r->DrawLine( start-camPos, start + end );
    //    //IEntity* ent = EntityManager::GetInstance()->ShootRay( start, end );
    //    //if ( ent )
    //    //{
    //    //    std::cout << "Entity: " << ent->GetName() << "\n";
    //    //    list.push_back( ent );
    //    //}
    //}
    //Vector2f p1( 257, 210);
    ////Vector2f p2( 200, 490 );
    //Vector2f p2( 420, 620);
    ////Vector2f p2( 320, 9490 );

    //Rect viewport( camPos.x+100, camPos.y+100, cam->GetViewPort().width-200, cam->GetViewPort().height-200 );
    ////if ( viewport.LineIntersects( p1, p2 ) )
    ////    std::cout << "intersected\n";
    //std::vector< tile_t* > list;
    //m_m_secondLayerQuadTree->GetObjects( p1, p2, list );
    ////m_m_secondLayerQuadTree->GetObjects( viewport, list );
    ////std::cout << "size: " << list.size() << " vs " << m_m_secondLayerQuadTree->Size() << "\n";
    //for(int i =list.size()-1; i >= 0; --i )
    //{
    //    tile_t* t = list[i];
    //    r->SetColorEffect( 0.75f, 0.72f, 0.73f, 0.834f );

    //    r->DrawSubImage( &t->image, t->m_bounds.x-( (int)camPos.x ),t->m_bounds.y- ( (int)camPos.y),1,1 );
    //    r->SetColorEffect( 1, 1, 1 );
    //}
    //r->DrawLine( p1-camPos, p2-camPos );
    ////r->DrawRect( viewport.x- camPos.x, viewport.y-camPos.y, viewport.width, viewport.height );
    //m_
    //g_effect.Update(1);
    //Rect e = g_effect.GetEffectSize();

    //Vector4f g = g_effect.GetCurrentColor();
    //r->SetColorEffect(  g[0], g[1],g[2], g[3] );
    
    //r->FillRect( e.x, e.y, e.width, e.height );
    
}

/** Ray Tracer - Returns a list of tiles it collides with */
void Scene::RayTrace( const Vector2f &start, const Vector2f &end, type_Tiles &tiles  ) const
{
    if( m_secondLayerQuadTree )
        m_secondLayerQuadTree->GetObjects( start, end, tiles );
}

Scene::~Scene(void)
{
    FreeScene();
}
